<template>
  <div class="swipe" ref="swipe">
    <div class="swipe__warrper clearfix"
         :style="warrperStyle"
         @touchstart='onTouchstart'
         @touchmove='onTouchmove'
         @touchend='onTouchend'
         @touchcancel='onTouchend'
         >
      <slot />
    </div>
    <div v-if="showDto" class="swipe__dots">
      <span v-for="(item, index) of dots" :key="index" :class="index === dotActive ? 'active' : ''"></span>
    </div>
  </div>
</template>
<script>
export default {
  props: {
    width: {
      type: Number
    },
    loop: {
      type: Boolean,
      default: false
    },
    showDto: {
      type: Boolean,
      default: false
    },
    duration: {
      type: Number,
      default: 500
    },
    autoPlay: {
      type: Number
    },
    bounce: {
      type: Boolean,
      default: true
    }
  },
  data() {
    return {
      swipes: [],
      active: 0,
      swipeItemWidth: 0,
      offset: 0,
      deltaX: 0,
      startX: 0,
      dots: [],
      swiping: false
    }
  },
  computed: {
    warrperStyle() {
      return {
        width: `${this.swipeItemWidth * this.swipes.length}px`,
        transform: `translate3d(${this.offset}px, 0, 0)`,
        'transition-duration': `${this.swiping ? this.duration : 0}ms`
      }
    },
    swipeWidth() {
      return this.$refs.swipe.offsetWidth
    },
    trackSize() {
      return this.swipes.length * this.swipeWidth
    },
    size() {
      return this.swipes.length - this.inPageItemNum
    },
    inPageItemNum() {
      return Math.floor(this.swipeWidth / this.swipeItemWidth)
    },
    dotActive() {
      return (this.active + this.swipes.length) % this.swipes.length
    },
    minOffset() {
      return this.swipes.length * this.swipeItemWidth - this.swipeItemWidth
    }
  },
  watch: {
    swipes() {
      this.init()
    }
  },
  mounted() {
    this.initDots()
  },
  methods: {
    init() {
      this.swipeItemWidth = this.width || this.$refs.swipe.offsetWidth

      this.swipes.forEach(swipe => {
        swipe.width = this.swipeItemWidth
      })

      this.autoPlayFn()
    },
    autoPlayFn() {
      if (!this.autoPlay) return
      clearInterval(this.timer)
      this.timer = setInterval(() => {
        this.resetTouchStatus()
        this.setPosition()
        if (!this.swiping) {
          setTimeout(() => {
            this.swiping = true
            this.move({ page: 1 })
          })
          return
        }
        this.move({ page: 1 })
      }, this.autoPlay)
    },
    initDots() {
      this.$nextTick(() => {
        if (!this.showDto) return
        this.dots = new Array(this.width ? this.size : this.swipes.length)
      })
    },
    resetTouchStatus() {
      this.deltaX = 0
      this.startX = 0
    },
    onTouchstart(event) {
      this.resetTouchStatus()
      this.startX = event.touches[0].clientX
      this.setPosition()
    },
    onTouchmove(event) {
      this.swiping = false
      const touche = event.touches[0]
      this.deltaX = touche.clientX - this.startX

      if (!this.bounce) {
        if (this.active === 0 && this.deltaX > 0) {
          this.deltaX = 0
        }
        if (this.active === this.size && this.deltaX < 0) {
          this.deltaX = 0
        }
      }

      this.move({
        offset: this.deltaX
      })
    },
    onTouchend() {
      const offsetX = Math.abs(this.deltaX)
      let page = 0
      if (offsetX > this.swipeWidth / 5) {
        page = 1
        if (offsetX > this.swipeItemWidth * 0.5) {
          page = Math.round(offsetX / this.swipeItemWidth)
        }
        // page = Math.round(offsetX / this.swipeItemWidth)
      }
      page = this.deltaX > 0 ? -page : page

      this.swiping = true
      this.move({
        page
      })
    },
    setPosition() {
      if (this.loop && !this.width) {
        if (this.active >= this.swipes.length) {
          this.offset = 0
          this.swipes[0].offset = 0
          this.active = 0
          this.swiping = false
        }
        if (this.active <= -1) {
          this.offset = -(this.swipeWidth - this.swipeItemWidth)
          this.active = this.swipes.length - 1
          this.swipes[this.swipes.length - 1].offset = 0
          this.swiping = false
        }
      }
    },
    move({ page, offset }) {
      const targetActive = this.getTargetActive(page)
      const targetOffset = this.getTargetOffset(targetActive, offset)
      this.active = targetActive
      this.offset = targetOffset
    },
    getTargetActive(page) {  // 0 1 -1
      if (page !== undefined) {
        const active = this.active + page
        if (this.width) {
          return active < 0 ? 0 : active > this.size ? this.size : active
        }
        return active
      }
      return this.active
    },
    getTargetOffset(active, offset = 0) {
      const currentPosition = active * this.swipeItemWidth
      let targetOffset = Math.round(offset - currentPosition)

      if (this.width && active > 0) {
        let diff = -(this.swipeWidth - this.inPageItemNum * this.swipeItemWidth) / 2
        if (targetOffset !== 0) {
          targetOffset = targetOffset - diff
        }
      } else {
        if (this.loop) {
          if (this.swipes[0] && targetOffset < -this.minOffset) {
            this.swipes[0].offset = this.trackSize
          }
          const len = this.swipes.length - 1
          if (this.swipes[len] && targetOffset > 0) {
            this.swipes[len].offset = -this.trackSize
          }
        }
      }
      return targetOffset
    }
  }
}
</script>
<style lang="less" scoped>
@import '~@/style/variable.less';

.swipe{
  position: relative;
  overflow: hidden;
  &__warrper{
    height: 100%;
  }
  &__dots{
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    bottom: 10px;
    >span{
      display: block;
      width: 6px;
      height: 6px;
      background-color: @swipe-dot-color;
      margin-right: 10px;
      border-radius: 100%;
    }
    .active{
      background-color: @swipe-dot-active;
    }
  }
}
</style>
